//
//  AppGraphManager.cpp
//  AppGraphManager is a thread that manages a simple directed graph
//  2006 John Wiggins
//
// This program is free software; you can redistribute it and/or modify  
// it under the terms of the GNU General Public License as published by  
// the Free Software Foundation; either version 2 of the License, or     
// (at your option) any later version.                                   
// 
// This program is distributed in the hope that it will be useful,       
// but WITHOUT ANY WARRANTY; without even the implied warranty of        
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         
// GNU General Public License for more details.                          
//     
//     You should have received a copy of the GNU General Public License     
//     along with this program; if not, write to the                         
//         Free Software Foundation, Inc.,                                       
//         59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.  
//-------------------------------------------------------------
#include <assert.h>

#include "App/AppGraphManager.h"
#include "App/AppMessageConstants.h"
#include "Ipc/IpcPort.h"
//-------------------------------------------------------------
AppGraphManager::AppGraphManager()
{
    // create the mixer thread
    mMixerThread = new AppMixerThread();

    // spawn our work thread
    if ( 0 != pthread_create( &mThread, NULL, start_loop, ( void * )this ) )
    {
        printf( "error creating the Graph Manager thread\n" );
        exit( -1 );
    }

    // create our message port
    // the name has to be unique
    mMsgPort = pp_create_port( "graph manager" );
    // check for error
    if ( mMsgPort < GE_NO_ERROR )
    {
        printf( "Error creating the Graph Manager thread's port. ( %d )\n", mMsgPort );
        exit( -1 );
    }

    // we aren't quitting yet
    mQuitting = false;
}
//-------------------------------------------------------------
AppGraphManager::~AppGraphManager()
{
    // kill the mixer thread
    delete mMixerThread;

    // kill the source threads
    for ( list<AppSourceThread*>::iterator s = mSourceThreads.begin();
        s != mSourceThreads.end(); ++s )
    {
        delete *s;
    }
    
    // send a quit message to the thread
    IpcMessage quitmsg( GE_QUIT );
    quitmsg.SendMessage( mMsgPort );

    // wait for the thread to exit
    pthread_join( mThread, NULL );

    // delete the message port
    pp_delete_port( mMsgPort );

    // delete all the new'd pointers
    for ( list<GraphVert*>::iterator v = mVertList.begin();
                v != mVertList.end(); ++v )
    {
        delete *v;
    }
}
//-------------------------------------------------------------
void *
AppGraphManager::start_loop( void *data )
{
    return ( ( AppGraphManager * )data )->main_loop();
}
//-------------------------------------------------------------
void *
AppGraphManager::main_loop()
{
    while ( true )
    {
        // receive any messages
        receiveMessages();

        // quit if we need to
        if ( mQuitting ) break;
    }

    return NULL;
}
//-------------------------------------------------------------
void
AppGraphManager::receiveMessages()
{
    int32 size, ret;
    uint32 code = 0;
    char *buffer = NULL;

    // read messages as long as there are messages to be read
    while ( ( size = pp_port_buffer_size( mMsgPort ) ) >= GE_NO_ERROR )
    {
        // allocate space for the message
        char *p = ( char * )realloc( buffer, size );
        if ( p == NULL )
        {
            printf( "realloc(  ) failure! %d : %s\n", __LINE__, __FILE__ );
            break;
        }
        buffer = p;

        // retrieve the message
        ret = pp_read_port( mMsgPort, &code, ( void * )buffer, size );

        // do something with the message
        if ( ret >= GE_NO_ERROR )
        {
            IpcMessage message( code );
            ret = message.Unflatten( buffer );
            if ( ret >= GE_NO_ERROR )
                handleMessage( &message );
        }

        // check for quit
        if ( mQuitting )
            return;
    }

    // free the message buffer
    free( buffer );
}
//-------------------------------------------------------------
void
AppGraphManager::handleMessage( IpcMessage * msg )
{
    switch ( msg->what )
    {
        case GE_QUIT:
        {
            mQuitting = true;
            break;
        }
        case GE_ADD_NODE:
        {
            // message contains:
            // - "type" : GE_INT32_TYPE : the type of node being added
            // - "plugin" : GE_POINTER_TYPE : a pointer to the plugin instance
            ge_plugin_type type;
            void *plugin;

            msg->FindInt32( "type", ( int32 * )&type );
            msg->FindPointer( "plugin", ( void const ** )&plugin );

            addNode( type, plugin );
            
            break;
        }
        case GE_REMOVE_NODE:
        {
            // message contains:
            // - "type" : GE_INT32_TYPE : the type of node being added
            // - "plugin" : GE_POINTER_TYPE : a pointer to the plugin instance
            ge_plugin_type type;
            void *plugin;

            msg->FindInt32( "type", ( int32 * )&type );
            msg->FindPointer( "plugin", ( void const ** )&plugin );

            removeNode( type, plugin );

            break;
        }
        case GE_ADD_EDGE:
        {
            // message contains:
            // - "to" : GE_POINTER_TYPE : a pointer to a plugin instance
            // - "from" : GE_POINTER_TYPE : a pointer to a plugin instance
            void *to, *from;

            msg->FindPointer( "to", ( void const ** )&to );
            msg->FindPointer( "from", ( void const ** )&from );

            if ( addEdge( from, to ) )
            {
                // let the source or mixer thread know what happened
            }

            break;
        }
        case GE_REMOVE_EDGE:
        {
            // message contains:
            // - "to" : GE_POINTER_TYPE : a pointer to a plugin instance
            // - "from" : GE_POINTER_TYPE : a pointer to a plugin instance
            void *to, *from;

            msg->FindPointer( "to", ( void const ** )&to );
            msg->FindPointer( "from", ( void const ** )&from );

            removeEdge( from, to );

            break;
        }
        case GE_ADD_SOURCE:
        {
            // a source thread has just accepted its source plugin
            // add the source to the node graph
            void *instance;
            ipc_port_t port;
    
            msg->FindPointer( "source", ( void const ** )&instance );
            msg->FindInt32( "reply", ( ipc_port_t * )&port );

            // add the plugin to the vertex list
            mVertList.push_back( 
                    new GraphVert(  instance, // the plugin instance
                                    GE_SOURCE_PLUGIN, // the type
                                    port, // the port id
                                    0, // the max # of inputs
                                    0, // the used # of inputs
                                    1, // the max # of outputs
                                    0 ) // the used # of outputs
                                    );
            break;
        }
        case GE_DESTROY_SOURCE:
        {
            break;
        }
        case GE_DESTROY_MIXER:
        {
            break;
        }
        case GE_DESTROY_FILTER:
        {
            break;
        }
        case GE_DESTROY_OUTPUT:
        {
            break;
        }
        default:
            break;
    }
}
//-------------------------------------------------------------
void
AppGraphManager::addSource( void *instance )
{
    // create a AppSourceThread object for this new source
    AppSourceThread *source = new AppSourceThread();

    // add the thread to the list of sources
    mSourceThreads.push_back( source );

    // tell the source about its plugin
    IpcMessage msg( GE_ADD_SOURCE );
    msg.AddPointer( "source", ( void const * )instance );
    msg.AddInt32( "reply", mMsgPort );
    source->postMessage( &msg );
    
}
//-------------------------------------------------------------
void
AppGraphManager::addMixer( void *instance )
{
    ge_mixer_plugin const *plug = ( ge_mixer_plugin * )instance;
    // add the plugin to the vertex list
    mVertList.push_back( 
            new GraphVert( instance, // the plugin instance
                            GE_MIXER_PLUGIN, // the type
                            -1, // the port id
                            plug->max_inputs, // the max # of inputs
                            0, // the used # of inputs
                            1, // the max # of outputs
                            0 ) // the used # of outputs
                            );
}
//-------------------------------------------------------------
void
AppGraphManager::addFilter( void *instance )
{
    // add the plugin to the vertex list
    mVertList.push_back( 
            new GraphVert( instance, // the plugin instance
                        GE_FILTER_PLUGIN, // the type
                        -1, // the port id
                        1, // the max # of inputs
                        0, // the used # of inputs
                        1, // the max # of outputs
                        0 ) // the used # of outputs
                        );
}
//-------------------------------------------------------------
void
AppGraphManager::addOutput( void *instance )
{
    // add the plugin to the vertex list
    mVertList.push_back( 
            new GraphVert( instance, // the plugin instance
                            GE_OUTPUT_PLUGIN, // the type
                            -1, // the port id
                            1, // the max # of inputs
                            0, // the used # of inputs
                            0, // the max # of outputs
                            0 ) // the used # of outputs
                            );
}
//-------------------------------------------------------------
void
AppGraphManager::removeSource( void *instance, ipc_port_t port )
{
    IpcMessage msg( GE_REMOVE_SOURCE );
    msg.AddPointer( "source", instance );
    msg.AddInt32( "reply", mMsgPort );
    msg.AddBool( "destroy", true );
    msg.SendMessage( port );
}
//-------------------------------------------------------------
void
AppGraphManager::removeMixer( void *instance, ipc_port_t port )
{
    IpcMessage msg( GE_REMOVE_MIXER );
    msg.AddPointer( "mixer", instance );
    msg.AddInt32( "reply", mMsgPort );
    msg.AddBool( "destroy", true );
    
    if ( port != -1 )
    {
        mMixerThread->postMessage( &msg );
    }
    else
    {
        // skip straight to the destruction if port is invalid
        msg.what = GE_DESTROY_MIXER;
        msg.SendMessage( mMsgPort );
    }
}
//-------------------------------------------------------------
void
AppGraphManager::removeFilter( void *instance, ipc_port_t port )
{
    IpcMessage msg( GE_REMOVE_FILTER );
    msg.AddPointer( "filter", instance );
    msg.AddInt32( "reply", mMsgPort );
    msg.AddBool( "destroy", true );

    if ( port != -1 )
    {
        msg.SendMessage( port );
    }
    else
    {
        // skip straight to the destruction if port is invalid
        msg.what = GE_DESTROY_FILTER;
        msg.SendMessage( mMsgPort );
    }
}
//-------------------------------------------------------------
void
AppGraphManager::removeOutput( void *instance, ipc_port_t port )
{
    IpcMessage msg( GE_DISCONNECT_OUTPUT );
    msg.AddPointer( "output", instance );
    msg.AddInt32( "reply", mMsgPort );
    msg.AddBool( "destroy", true );
    
    if ( port != -1 )
    {
        mMixerThread->postMessage( &msg );
    }
    else
    {
        // skip straight to the destruction if port is invalid
        msg.what = GE_DESTROY_OUTPUT;
        msg.SendMessage( mMsgPort );
    }
}
//-------------------------------------------------------------
void
AppGraphManager::addNode( ge_plugin_type type, void *instance )
{
    switch ( type )
    {
        case GE_SOURCE_PLUGIN:
            addSource( instance );
            break;
        case GE_MIXER_PLUGIN:
            addMixer( instance );
            break;
        case GE_FILTER_PLUGIN:
            addFilter( instance );
            break;
        case GE_OUTPUT_PLUGIN:
            addOutput( instance );
            break;
        default:
            break;
    }
}
//-------------------------------------------------------------
void
AppGraphManager::removeNode( ge_plugin_type type, void *instance )
{
    GraphVert *remVert = NULL;
    list<GraphVert*>::iterator verts;
    
    // find the vertex and remove it from the vertex list
    for ( verts = mVertList.begin(); verts != mVertList.end(); ++verts )
    {
        if ( ( *verts )->mInstance == instance )
        {
            remVert = *verts;
            mVertList.erase( verts );
            break;
        }
    }

    // if the vertex doesn't exist, there is nothing to do
    if ( remVert == NULL )
    {
        return;
    }
    
    // first, pass the news on to any threads that are using this plugin
    switch ( type )
    {
        case GE_SOURCE_PLUGIN:
            removeSource( instance, remVert->mPort );
            break;
        case GE_MIXER_PLUGIN:
            removeMixer( instance, remVert->mPort );
            break;
        case GE_FILTER_PLUGIN:
            removeFilter( instance, remVert->mPort );
            break;
        case GE_OUTPUT_PLUGIN:
            removeOutput( instance, remVert->mPort );
            break;
        default:
            break;
    }

    // then, remove all the edges connected to this vertex
    removeAllGraphEdgesForNode( remVert );

    // delete the vertex
    delete remVert;
}
//-------------------------------------------------------------
bool
AppGraphManager::addEdge( void *from, void *to )
{
    GraphVert *fromVert = NULL, *toVert = NULL;
    list<GraphVert*>::iterator verts;

    // find the Verts containing from and to
    for ( verts = mVertList.begin(); verts != mVertList.end(); ++verts )
    {
        if ( ( *verts )->mInstance == from )
        {
            fromVert = *verts;
        }
        else if ( ( *verts )->mInstance == to )
        {
            toVert = *verts;
        }

        if ( fromVert != NULL && toVert != NULL )
        {
            break;
        }
    }

    // we can't create an edge between vertices if they can't be found
    if ( fromVert == NULL || toVert == NULL )
    {
        return false;
    }
    
    // make sure an edge can even be created here
    // we will not create an edge if either node has their slots full
    if ( fromVert->mMaxOutputs == fromVert->mUsedOutputs )
    {
        return false;
    }
    if ( toVert->mMaxInputs == toVert->mUsedInputs )
    {
        return false;
    }

    // make sure the out-edge fromVert -> toVert doesn't exist
    if ( mOutEdgeMap.count( fromVert ) > 0 )
    {
        for ( verts = mOutEdgeMap[fromVert].begin();
                    verts != mOutEdgeMap[fromVert].end(); ++verts )
        {
            // just bail out if this edge already exists
            if ( *verts == toVert )
            {
                return false;
            }
        }
    }
    
    // and make sure the out-edge toVert -> fromVert doesn't exist either
    if ( mOutEdgeMap.count( toVert ) > 0 )
    {
        for ( verts = mOutEdgeMap[toVert].begin();
                    verts != mOutEdgeMap[toVert].end(); ++verts )
        {
            // just bail out if this edge already exists
            if ( *verts == fromVert )
            {
                return false;
            }
        }
    }

    // add an out edge
    if ( mOutEdgeMap.count( fromVert ) == 0 )
    {
        mOutEdgeMap[fromVert] = list<GraphVert *>();
    }
    mOutEdgeMap[fromVert].push_back( toVert );
    // and an in edge
    if ( mInEdgeMap.count( toVert ) == 0 )
    {
        mInEdgeMap[toVert] = list<GraphVert *>();
    }
    mInEdgeMap[toVert].push_back( fromVert );
    
    // update the affected vertices
    fromVert->mUsedOutputs++;
    toVert->mUsedInputs++;

    // tell the source or mixer thread what happened
    sendConnectionMessage( fromVert, toVert );

    return true;
}
//-------------------------------------------------------------
bool
AppGraphManager::removeEdge( void *from, void *to )
{
    GraphVert *fromVert = NULL, *toVert = NULL;
    list<GraphVert*>::iterator verts;

    // find the Verts containing from and to
    for ( verts = mVertList.begin(); verts != mVertList.end(); ++verts )
    {
        if ( ( *verts )->mInstance == from )
        {
            fromVert = *verts;
        }
        else if ( ( *verts )->mInstance == to )
        {
            toVert = *verts;
        }

        if ( fromVert != NULL && toVert != NULL )
        {
            break;
        }
    }

    // we can't remove an edge between vertices if they can't be found
    if ( fromVert == NULL || toVert == NULL )
    {
        return false;
    }
    
    if ( removeGraphEdge( fromVert, toVert ) )
    {
        sendDisconnectionMessage( fromVert, toVert );
        return true;
    }
    
    return false;
}
//-------------------------------------------------------------
bool
AppGraphManager::removeGraphEdge( GraphVert *fromVert, GraphVert *toVert )
{
    list<GraphVert*>::iterator verts;
    bool removed = false;

    // we can't remove an edge if it doesn't exist either
    if ( mOutEdgeMap.count( fromVert ) == 0 )
    {
        return false;
    }

    // find the out-edge and remove it
    for ( verts = mOutEdgeMap[fromVert].begin();
                verts != mOutEdgeMap[fromVert].end(); ++verts )
    {
        // the edge was found. erase it.
        if ( *verts == toVert )
        {
            mOutEdgeMap[fromVert].erase( verts );
            // update fromVert
            fromVert->mUsedOutputs--;
            removed = true;
            break;
        }
    }

    // we don't need to remove an in-edge if no out-edge was removed
    if ( removed )
    {
        // find the in-edge and remove it
        for ( verts = mInEdgeMap[toVert].begin();
                    verts != mInEdgeMap[toVert].end(); ++verts )
        {
            // the edge was found. erase it
            if ( *verts == fromVert )
            {
                mInEdgeMap[toVert].erase( verts );
                // update toVert
                toVert->mUsedInputs--;
                break;
            }
        }
    }

    return removed;
}
//-------------------------------------------------------------
void
AppGraphManager::removeAllGraphEdgesForNode( GraphVert *remVert )
{
    list<GraphVert*>::iterator ivert, overt;

    // remove all in- and out-edges associated with the vertex
    //
    // decrement the in-edge count on all the vertices pointed to by this vertex
    if ( mOutEdgeMap.count( remVert ) > 0 )
    {
        for ( overt = mOutEdgeMap[remVert].begin();
                    overt != mOutEdgeMap[remVert].end(); ++overt )
        {
            // remove the in-edge ( to remVert ) from *overt
            for ( ivert = mInEdgeMap[*overt].begin();
                        ivert != mInEdgeMap[*overt].end(); ++ivert )
            {
                if ( *ivert == remVert )
                {
                    mInEdgeMap[*overt].erase( ivert );
                    break;
                }
            }
            // decrement the in-edge count on *overt
            ( *overt )->mUsedInputs--;
        }
        // then remove the edges leaving this vertex
        mOutEdgeMap.erase( remVert );
    }

    // decrement the out-edge count on all the vertices pointing to this vertex
    if ( mInEdgeMap.count( remVert ) > 0 )
    {
        for ( ivert = mInEdgeMap[remVert].begin();
                    ivert != mInEdgeMap[remVert].end(); ++ivert )
        {
            // remove the out-edge ( to remVert ) from *ivert
            for ( overt = mOutEdgeMap[*ivert].begin();
                        overt != mOutEdgeMap[*ivert].end(); ++overt )
            {
                if ( *overt == remVert )
                {
                    mOutEdgeMap[*ivert].erase( overt );
                    break;
                }
            }
            // decrement the out-edge count on *ivert
            ( *ivert )->mUsedOutputs--;
        }
        // then remove the edges entering this vertex
        mInEdgeMap.erase( remVert );
    }
}
//-------------------------------------------------------------
uint32
AppGraphManager::countUpstreamMixers( GraphVert *vert )
{
    uint32 here = ( vert->mType == GE_MIXER_PLUGIN ) ? 1 : 0;

    // base case
    if ( vert->mUsedInputs == 0 )
    {
        return here;
    }
    
    // recursive case
    uint32 ret=0;
    list<GraphVert*>::iterator i = mInEdgeMap[vert].begin();
    if ( vert->mUsedInputs == 1 )
    {
        return here + countUpstreamMixers( *i );
    }
    else
    {
        for ( ; i != mInEdgeMap[vert].end(); ++i )
        {
            uint32 tmp = countUpstreamMixers( *i );
            if ( tmp > ret )
            {
                ret = tmp;
            }
        }
    }
    
    return here + ret;
}
//-------------------------------------------------------------
AppGraphManager::GraphVert *
AppGraphManager::findUpstreamSourceOrMixer( GraphVert *vert )
{
    // base cases
    if ( vert->mType == GE_MIXER_PLUGIN || vert->mType == GE_SOURCE_PLUGIN )
    {
        return vert;
    }
    else if ( vert->mUsedInputs == 0 )
    {
        return NULL;
    }
    
    // recursive case
    GraphVert *ret = NULL;
    list<GraphVert*>::iterator i = mInEdgeMap[vert].begin();

    for ( ; i != mInEdgeMap[vert].end(); ++i )
    {
        GraphVert *tmp = findUpstreamSourceOrMixer( *i );

        if ( tmp != NULL )
        {
            // prefer mixers over sources
            // if ANY mixer is found it should be returned
            if ( ret == NULL )
            {
                ret = tmp;
            }
            else if ( ret->mType == GE_SOURCE_PLUGIN
                        && tmp->mType == GE_MIXER_PLUGIN )
            {
                ret = tmp;
            }
            else // mixer:mixer or source:source or mixer:source
            {
                ; // do nothing, we're trying to find mixers
            }
        }
    }
    
    return ret;
}
//-------------------------------------------------------------
void
AppGraphManager::findAllUpstreamMixers( GraphVert *vert,
                            list<GraphVert*> & mixers )
{
    // recurse up the graph
    if ( vert->mUsedInputs > 0 )
    {
        list<GraphVert*>::iterator i = mInEdgeMap[vert].begin();

        // loop over all the inputs
        for ( ; i != mInEdgeMap[vert].end(); ++i )
        {
            findAllUpstreamMixers( *i, mixers );
        }
    }
    
    // is this vert a mixer?
    // if so, add it to the list
    if ( vert->mType == GE_MIXER_PLUGIN )
    {
        mixers.push_back( vert );
    }
}
//-------------------------------------------------------------
void
AppGraphManager::getDirectlyAttachedMixers( GraphVert *vert,
                            list<GraphVert*> & mixers )
{
}
//-------------------------------------------------------------
void
AppGraphManager::sendDisconnectionMessage( GraphVert *fromVert, GraphVert *toVert )
{
}
//-------------------------------------------------------------
void
AppGraphManager::sendConnectionMessage( GraphVert *fromVert, GraphVert *toVert )
{
    GraphVert *rcptVert = NULL;
    uint32 what;

    // if connecting a source to a filter, send a message to the source
    // all other cases, send a message to the mixer
    
    // in all cases, we need to find the nearest source or mixer
    // this will only return a source if there are zero mixers upstream
    rcptVert = findUpstreamSourceOrMixer( fromVert );

    if ( toVert->mType == GE_FILTER_PLUGIN )
    {
        // if connecting to a filter, always walk up the graph to the nearest
        // source or mixer ( which might not exist! )
        if ( rcptVert == NULL )
        {
            ; // do nothing, wait until a full connection has been made
        }
        else if ( rcptVert->mType == GE_SOURCE_PLUGIN )
        {
            // we found a source
            // make sure the source knows about all the nodes in this chain
            notifySourceOfFilterChain( rcptVert );
        }
        else if ( rcptVert->mType == GE_MIXER_PLUGIN )
        {
            // we found a mixer
            // make sure the mixer thread knows about this node chain
            notifyMixerOfFilterChain( rcptVert );
        }
    }
    else if ( toVert->mType == GE_MIXER_PLUGIN )
    {
        // walk the graph to find the first source or mixer
        // if a mixer is found, you'll message the mixer thread
        // if a source is found, you'll message the mixer thread ( about the source )
        // if neither is found, nobody gets a message!
        if ( rcptVert == NULL )
        {
            ; // nobody gets a message
        }
        else if ( rcptVert->mType == GE_SOURCE_PLUGIN )
        {
            // a source has been attached to a mixer
            // make sure the source knows about it's entire chain
            // then notify the mixer of its new source
            notifySourceOfFilterChain( rcptVert );
            notifyMixerOfSource( toVert, rcptVert );
        }
        else if ( rcptVert->mType == GE_MIXER_PLUGIN )
        {
            // another mixer has been connected...
            // figure out how the mixers are arranged and let the mixer thread
            // know about it.
            // make sure the mixer thread has this mixer
            confirmMixerResidency( toVert );
            // then adjust the order in which the mixers are run ( if needed )
            updateMixerOrder( toVert );
        }
    }
    else if ( toVert->mType == GE_OUTPUT_PLUGIN )
    {
        // an output plugin runs in the mixer threadVert
        // If rcptVert is a source, make sure the mixer thread knows about it
        // In any case, let the mixer thread know that it has an output
        if ( rcptVert == NULL )
        {
            ; // do nothing... really
        }
        else if ( rcptVert->mType == GE_MIXER_PLUGIN )
        {
            // XXX: what if an output has already been connected???
            // let the mixer thread know that an output has been connected
            notifyMixerThreadOfOutput( toVert );
        }
    }
}
//-------------------------------------------------------------
void
AppGraphManager::notifySourceOfFilterChain( GraphVert *srcVert )
{
}
//-------------------------------------------------------------
void
AppGraphManager::notifySourceOfOutput( GraphVert *srcVert )
{
    // message contains:
    // what = GE_CONNECT_OUTPUT
    // - "ouput" : GE_POINTER_TYPE : a pointer to the ouput instance
}
//-------------------------------------------------------------
void
AppGraphManager::notifyMixerOfFilterChain( GraphVert *mixVert )
{
}
//-------------------------------------------------------------
void
AppGraphManager::notifyMixerOfSource( GraphVert *mixVert, GraphVert *srcVert )
{
    // message contains:
    // what = GE_CONNECT_SOURCE:
    // - "source" : GE_POINTER_TYPE : a pointer to a AppSourceThread
    // - "mixer" : GE_POINTER_TYPE : a pointer to a mixer instance
    
    // make sure mixVert is hooked up
    assert( mixVert->mPort != -1 );

    IpcMessage msg( GE_CONNECT_SOURCE );

    msg.AddPointer( "source", ( void const * )srcVert->mInstance );
    msg.AddPointer( "mixer", ( void const * )mixVert->mInstance );

    // send it to the mixer
    mMixerThread->postMessage( &msg );
}
//-------------------------------------------------------------
void
AppGraphManager::confirmMixerResidency( GraphVert *mixVert )
{
    // message contains:
    // what = GE_ADD_MIXER:
    // - "mixer" : GE_POINTER_TYPE : a pointer to the mixer instance

    // does this mixer node have a valid port number?
    // if not, tell the mixer thread about it
    if ( mixVert->mPort == -1 )
    {
        IpcMessage msg( GE_ADD_MIXER );
        msg.AddPointer( "mixer", ( void const * )mixVert->mInstance );

        // send it to the mixer
        mMixerThread->postMessage( &msg );
    }
}

// this function object is for the updateMixerOrder function below
/*struct mixSort
    : public binary_function<GraphVert*, GraphVert*, bool>
{
    mixSort( map<GraphVert*, uint32> *countMap ) : mCountMap( countMap ){}
    map<GraphVert*, uint32> *mCountMap;
    bool operator(  )( GraphVert* x, GraphVert* y )
    { return ( ( *mCountMap )[x] < ( *mCountMap )[y] ); }
};*/
//-------------------------------------------------------------
void
AppGraphManager::updateMixerOrder( GraphVert *mixVert )
{
    // mixVert is some mixer in the chain
    // message contains:
    // what = GE_MIXER_ORDERING
    // - "mixers" : GE_POINTER_TYPE : a list of pointers to mixer instances
    //  ( the mixers are stored in the order they should run )
    // - "0" : GE_INT32_TYPE : a list of indices into the "mixers" list
    // - ... : GE_INT32_TYPE : a list of indices into the "mixers" list
    // - "n" : GE_INT32_TYPE : a list of indices into the "mixers" list
    
    GraphVert *lastMixer = mixVert, *curVert = mixVert;
    list<GraphVert*> mixerChain;
    map<GraphVert*,uint32> upstreamCounts;

    // find the last mixer in the chain
    while ( curVert != NULL )
    {
        if ( curVert->mType == GE_MIXER_PLUGIN )
        {
            lastMixer = curVert;
        }

        if ( curVert->mUsedOutputs != 0 )
        {
            curVert = mOutEdgeMap[curVert].front();
        }
        else
        {
            curVert = NULL;
        }
    }
    // get a list of all the mixers in this chain
    findAllUpstreamMixers( lastMixer, mixerChain );
    // get upstream mixer counts for each of the mixers in the list
    list<GraphVert*>::iterator i = mixerChain.begin();
    for ( ; i != mixerChain.end(); ++i )
    {
        upstreamCounts[*i] = countUpstreamMixers( *i );
    }
    // sort the list of mixers
    // XXX: finish this monstrousity
}
//-------------------------------------------------------------
void
AppGraphManager::notifyMixerThreadOfOutput( GraphVert *outVert )
{
    // message contains:
    // what = GE_CONNECT_OUTPUT
    // - "ouput" : GE_POINTER_TYPE : a pointer to the ouput instance
}
//-------------------------------------------------------------
